/*
 * 文件名称:          ProxySessionFactoryBuilder.java
 * 版权所有@ 2016——2017 无锡聆音科技有限公司，保留所有权利
 * 时间:             2016年7月27日 下午9:21:11
 */
package com.lingyin.database.transaction.hibernate;

import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.Interceptor;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.NamingStrategy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;

/**
 *   该类进行sessionfactory的实例构建，在设置相关的属性值后，
 *  可以通过调用该类的afterPropertiesSet()进行sessionfactory的
 *  重新构建，构建成功后即可通过getSessionFactory()方法获取新
 *  的sessionfactory对象
 * 
 * @项目        hibernate.database.transaction
 * 
 * @作者:       关宏新
 * 
 * @日期:       2016年7月27日
 * 
 * @负责人:    关宏新
 * 
 * @负责小组:   聆音项目组
 * 
 */
public class ProxySessionFactoryBuilder implements ApplicationContextAware, 
                                InitializingBean, IProxySessionFactoryBuilder, ResourceLoaderAware
{
    private ApplicationContext applicationContext;
    private DataSource dataSource;
    private Resource[] configLocations;
    private String[] mappingResources;
    private Resource[] mappingLocations;
    private Resource[] cacheableMappingLocations;
    private Resource[] mappingJarLocations;
    private Resource[] mappingDirectoryLocations;
    private Interceptor entityInterceptor;
    private NamingStrategy namingStrategy;
    private Properties hibernateProperties;
    private Class<?>[] annotatedClasses;
    private String[] annotatedPackages;
    private String[] packagesToScan;
    private Object jtaTransactionManager;
    private ResourcePatternResolver resourcePatternResolver;
    private SessionFactory sessionFactory;
    //private LocalSessionFactoryBean springSessionFactory;

    /**
     * 
     */
    public ProxySessionFactoryBuilder()
    {
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setApplicationContext(org.springframework.context.ApplicationContext)
     * 
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        this.applicationContext = applicationContext;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setDataSource(javax.sql.DataSource)
     * 
     */
    @Override
    public void setDataSource(DataSource dataSource)
    {
        this.dataSource = dataSource;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setConfigLocations(org.springframework.core.io.Resource[])
     * 
     */
    @Override
    public void setConfigLocations(Resource[] configLocations)
    {
        this.configLocations = configLocations;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addConfigLocations(java.lang.String)
     * 
     */
    @Override
    public void addConfigLocations(String ... locations)
    {
        if (locations != null)
        {
            Resource[] temp = getResources(locations);
            configLocations = merge(configLocations, temp);
        }
    }    

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setMappingResources(java.lang.String[])
     * 
     */
    @Override
    public void setMappingResources(String[] mappingResources)
    {
        this.mappingResources = mappingResources;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addMappingResources(java.lang.String[])
     * 
     */
    @Override
    public void addMappingResources(String ... resources)
    {
        if (resources != null)
        {
            mappingResources = merge(mappingResources, resources);
        }
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setMappingLocations(org.springframework.core.io.Resource[])
     * 
     */
    @Override
    public void setMappingLocations(Resource[] mappingLocations)
    {
        this.mappingLocations = mappingLocations;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addMappingLocations(java.lang.String)
     * 
     */
    @Override
    public void addMappingLocations(String ... addMappingLocations)
    {
        if (addMappingLocations != null)
        {
            Resource[] temp = getResources(addMappingLocations);
            mappingLocations = merge(mappingLocations, temp);
        }
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setCacheableMappingLocations(org.springframework.core.io.Resource[])
     * 
     */
    @Override
    public void setCacheableMappingLocations(Resource[] cacheableMappingLocations)
    {
        this.cacheableMappingLocations = cacheableMappingLocations;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addCacheableMappingLocations(java.lang.String)
     * 
     */
    @Override
    public void addCacheableMappingLocations(String ... mappingLocations)
    {
        if (mappingLocations != null)
        {
            Resource[] temp = getResources(mappingLocations);
            cacheableMappingLocations = merge(cacheableMappingLocations, temp);
        }
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setMappingJarLocations(org.springframework.core.io.Resource[])
     * 
     */
    @Override
    public void setMappingJarLocations(Resource[] mappingJarLocations)
    {
        this.mappingJarLocations = mappingJarLocations;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addMappingJarLocations(java.lang.String)
     * 
     */
    @Override
    public void addMappingJarLocations(String ... addMappingJarLocations)
    {
        if (addMappingJarLocations != null)
        {
            Resource[] temp = getResources(addMappingJarLocations);
            mappingJarLocations = merge(mappingJarLocations, temp);
        }
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setMappingDirectoryLocations(org.springframework.core.io.Resource[])
     * 
     */
    @Override
    public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations)
    {
        this.mappingDirectoryLocations = mappingDirectoryLocations;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addMappingDirectoryLocations(java.lang.String)
     * 
     */
    @Override
    public void addMappingDirectoryLocations(String ... addmappingDirectoryLocations)
    {
        if (addmappingDirectoryLocations != null)
        {
            Resource[] temp = getResources(addmappingDirectoryLocations);
            mappingDirectoryLocations = merge(mappingDirectoryLocations, temp);
        }
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setEntityInterceptor(org.hibernate.Interceptor)
     * 
     */
    @Override
    public void setEntityInterceptor(Interceptor entityInterceptor)
    {
        this.entityInterceptor = entityInterceptor;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setNamingStrategy(org.hibernate.cfg.NamingStrategy)
     * 
     */
    @Override
    public void setNamingStrategy(NamingStrategy namingStrategy)
    {
        this.namingStrategy = namingStrategy;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setHibernateProperties(java.util.Properties)
     * 
     */
    @Override
    public void setHibernateProperties(Properties hibernateProperties)
    {
        this.hibernateProperties = hibernateProperties;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setAnnotatedClasses(java.lang.Class)
     * 
     */
    @Override
    public void setAnnotatedClasses(Class< ? >[] annotatedClasses)
    {
        this.annotatedClasses = annotatedClasses;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addAnnotatedClasses(java.lang.Class)
     * 
     */
    @Override
    public void addAnnotatedClasses(Class< ? > ... addAnnotatedClasses)
    {
        this.annotatedClasses = merge(this.annotatedClasses, addAnnotatedClasses);
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setAnnotatedPackages(java.lang.String[])
     * 
     */
    @Override
    public void setAnnotatedPackages(String[] annotatedPackages)
    {
        this.annotatedPackages = annotatedPackages;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addAnnotatedPackages(java.lang.String[])
     * 
     */
    @Override
    public void addAnnotatedPackages(String ... addAnnotatedPackages)
    {
        this.annotatedPackages = merge(this.annotatedPackages, addAnnotatedPackages);
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setPackagesToScan(java.lang.String[])
     * 
     */
    @Override
    public void setPackagesToScan(String[] packagesToScan)
    {
        this.packagesToScan = packagesToScan;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#addPackagesToScan(java.lang.String[])
     * 
     */
    @Override
    public void addPackagesToScan(String ... addPackagesToScan)
    {
        this.packagesToScan = merge(this.packagesToScan, addPackagesToScan);
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setJtaTransactionManager(java.lang.Object)
     * 
     */
    @Override
    public void setJtaTransactionManager(Object jtaTransactionManager)
    {
        this.jtaTransactionManager = jtaTransactionManager;
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#setResourceLoader(org.springframework.core.io.ResourceLoader)
     * 
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) 
    {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    }

    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#getSessionFactory()
     * 
     */
    @Override
    public SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
    
    /** 
     *(non-Javadoc)
     * @see com.sugon.database.service.transaction.hibernate.IProxySessionFactoryBuilder#afterPropertiesSet()
     * 
     */
    @Override
    public void afterPropertiesSet() throws IOException 
    {
        LocalSessionFactoryBean springSessionFactory = new LocalSessionFactoryBean();
        springSessionFactory.setDataSource(dataSource);
        springSessionFactory.setConfigLocations(configLocations);
        springSessionFactory.setMappingResources(mappingResources);
        springSessionFactory.setMappingLocations(mappingLocations);
        springSessionFactory.setCacheableMappingLocations(cacheableMappingLocations);
        springSessionFactory.setMappingJarLocations(mappingJarLocations);
        springSessionFactory.setMappingDirectoryLocations(mappingDirectoryLocations);
        springSessionFactory.setEntityInterceptor(entityInterceptor);
        springSessionFactory.setNamingStrategy(namingStrategy);
        springSessionFactory.setHibernateProperties(hibernateProperties);
        springSessionFactory.setAnnotatedClasses(annotatedClasses);
        springSessionFactory.setAnnotatedPackages(annotatedPackages);
        springSessionFactory.setPackagesToScan(packagesToScan);
        springSessionFactory.setJtaTransactionManager(jtaTransactionManager);
        if (resourcePatternResolver != null)
        {
            springSessionFactory.setResourceLoader(resourcePatternResolver);
        }
        springSessionFactory.afterPropertiesSet();
        sessionFactory = springSessionFactory.getObject();
        
    }
    
    private Resource[] getResources(String ... resource)
    {
        if (resource == null)
        {
            return null;
        }
        Resource[] ret = null;
        Resource[] tempR;
        for (String temp : resource)
        {
            try
            {
                tempR = applicationContext.getResources(temp);
                ret = merge(ret, tempR);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
        return ret;
    }
    
    private Resource[] merge(Resource[] first, Resource[] two)
    {
        if (first == null)
        {
            return two;
        }
        if (two == null)
        {
            return first;
        }
        int size1 = first.length;
        int size2 = two.length;
        Resource[] ret = new Resource[size1 + size2];
        System.arraycopy(first, 0, ret, 0, size1);
        System.arraycopy(two, 0, ret, size1, size2);
        return ret;
    }
    
    private String[] merge(String[] first, String[] two)
    {
        if (first == null)
        {
            return two;
        }
        if (two == null)
        {
            return first;
        }
        int size1 = first.length;
        int size2 = two.length;
        String[] ret = new String[size1 + size2];
        System.arraycopy(first, 0, ret, 0, size1);
        System.arraycopy(two, 0, ret, size1, size2);
        return ret;
    }
    
    private Class<?>[] merge(Class<?>[] first, Class<?>[] two)
    {
        if (first == null)
        {
            return two;
        }
        if (two == null)
        {
            return first;
        }
        int size1 = first.length;
        int size2 = two.length;
        Class<?>[] ret = new Class<?>[size1 + size2];
        System.arraycopy(first, 0, ret, 0, size1);
        System.arraycopy(two, 0, ret, size1, size2);
        return ret;
    }
    
}
